package com.gurun.camera.device.model.impl;

import android.support.annotation.NonNull;
import android.util.Log;

import com.danale.player.entity.CloudRecordDevice;
import com.danale.player.entity.SdRecord;
import com.danale.player.entity.SdRecordDevice;
import com.danale.sdk.Danale;
import com.danale.sdk.cloud.player.CloudRecordPlayback;
import com.danale.sdk.device.bean.CmdDeviceInfo;
import com.danale.sdk.device.bean.RecordInfo;
import com.danale.sdk.device.constant.DeviceType;
import com.danale.sdk.device.constant.LiveType;
import com.danale.sdk.device.service.request.GetBaseInfoRequest;
import com.danale.sdk.device.service.request.GetSdcStatusRequest;
import com.danale.sdk.device.service.request.RecordListRequest;
import com.danale.sdk.device.service.response.GetBaseInfoResponse;
import com.danale.sdk.device.service.response.RecordListResponse;
import com.danale.sdk.platform.cache.DeviceCache;
import com.danale.sdk.platform.constant.cloud.CloudRecordType;
import com.danale.sdk.platform.constant.cloud.RecordType;
import com.danale.sdk.platform.entity.cloud.CloudRecord;
import com.danale.sdk.platform.entity.cloud.CloudRecordPlayInfo;
import com.danale.sdk.platform.entity.device.Device;
import com.danale.sdk.platform.entity.v3.PushMsg;
import com.danale.sdk.platform.result.cloud.GetCloudRecordListResult;
import com.danale.sdk.platform.result.cloud.GetCloudRecordPlayInfoResult;
import com.danale.sdk.platform.result.cloud.GetDeviceServersResult;
import com.danale.sdk.throwable.PlatformApiError;
import com.gurun.camera.device.bean.CloudRecordInfo;
import com.gurun.camera.device.constant.AchieveType;
import com.gurun.camera.device.constant.VideoDataType;
import com.gurun.camera.device.model.ControlManager;
import com.gurun.camera.device.util.CloudHelper;
import com.gurun.camera.model.DeviceCloudInfo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

import static com.gurun.camera.device.constant.VideoDataType.CLOUD;
import static com.gurun.camera.device.constant.VideoDataType.CLOUD_SINGLE;
import static com.gurun.camera.device.constant.VideoDataType.DISK;

public class CloudSDControlManager extends ControlManager {

    private CloudRecordDevice cloudRecordDevice;
    private List<CloudRecordPlayInfo> cloudRecordPlayInfoList;
    private List<SdRecord> sdRecordList;
    private ArrayList<CloudRecordInfo> cloudRecordInfoList;
    private ArrayList<RecordInfo> recordInfoList;

    private long timestamp;

    public CloudSDControlManager(VideoDataType type){
        videoDataType = type;
        videoPlayModel = new VideoPlayerModel(type);
    }

    @Override
    public void setId(String id) {
        super.setId(id);
        device = DeviceCache.getInstance().getDevice(id);
        List<Device> devices = new ArrayList<>();
        devices.add(device);
        videoPlayModel.setDevices(devices);
        setCurrentVideoDevice(videoPlayModel.getDevices().get(0));
        switch (videoDataType){
            case CLOUD_SINGLE:
            case CLOUD:
                cloudRecordPlayInfoList = new ArrayList<>();
                break;
            case DISK:
                sdRecordList = new ArrayList<>();
                recordInfoList = new ArrayList<>();
                break;
        }
        cloudRecordInfoList = new ArrayList<>();
    }

    @Override
    public void getCloudState() {
        Log.d("cloudinfo", "getCloudState");
        CloudHelper.getCloudInfoByDevice(device, AchieveType.SERVER_ONLY)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<DeviceCloudInfo>() {
                    @Override
                    public void call(DeviceCloudInfo deviceCloudInfo) {
                        if (deviceCloudInfo != null){
                            if (onCallbackListener != null) {
                                onCallbackListener.onCloudStateCallback(deviceCloudInfo);
                            }
                        }
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        Log.d("", "");
                    }
                });
    }

    @Override
    public void getCloudStateByList(List<Device> devices) {
        super.getCloudStateByList(devices);
        CloudHelper.getCloudInoByDevList(devices, AchieveType.DEFAULT)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<List<DeviceCloudInfo>>() {
                    @Override
                    public void call(List<DeviceCloudInfo> deviceCloudInfos) {
                        if (deviceCloudInfos != null){
                            if (onCallbackListener != null){
                                onCallbackListener.onCloudStateListCallback(deviceCloudInfos);
                            }
                        }
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {

                    }
                });
    }

    @Override
    public void getSdState() {
        super.getSdState();

        GetSdcStatusRequest request = new GetSdcStatusRequest();
        request.setChannelNo(1);

        getBaseInfo();
    }

    void getBaseInfo(){
        GetBaseInfoRequest request = new GetBaseInfoRequest();
        request.setCh_no(1);
        Danale.get().getDeviceSdk().command().getBaseInfo(getCmdDeviceInfo(), request)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<GetBaseInfoResponse>() {
                    @Override
                    public void call(GetBaseInfoResponse response) {
                        if (onCallbackListener != null){
                            onCallbackListener.onSDStateCallback(response);
                        }
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {}
                });

    }

    @Override
    public void getSDRecordList(long timestamp, int channel) {
        super.getSDRecordList(timestamp, channel);
        Log.d("channel", "channel : " + channel);
        cloudRecordInfoList.clear();

        this.timestamp = timestamp/1000;
        startDayTime = this.timestamp;
        endDayTime = startDayTime + 24 * 3600;

        getSdRecordList(timestamp, channel);
    }

    @Override
    public void getSDRecordList(long millTimestamps) {
        super.getSDRecordList(millTimestamps);
        getSDRecordList(millTimestamps, 1);
    }

    private long startDayTime;
    private long endDayTime;

    private void getSdRecordList(long millTimestamps, final int channel) {
        recordInfoList.clear();
        this.timestamp = millTimestamps/1000;
        RecordListRequest request = new RecordListRequest();
        request.setCh_no(channel);
        request.setGet_num(30);
        request.setGet_type(RecordListRequest.GET_TYPE_NEXT);
        request.setLast_time(timestamp);

        Danale.get().getDeviceSdk().command().recordList(getCmdDeviceInfo(), request)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(new Func1<RecordListResponse, Observable<RecordInfo>>() {
                    @Override
                    public Observable<RecordInfo> call(RecordListResponse response) {

                        Observable.from(response.getRec_lists())
                                .subscribe(new Action1<RecordInfo>() {
                                    @Override
                                    public void call(RecordInfo recordInfo) {
                                        recordInfoList.add(recordInfo);
                                    }
                                });


                        if (recordInfoList == null || recordInfoList.size() == 0) {

                        } else if (recordInfoList.get(recordInfoList.size() - 1).getStart_time() >= endDayTime
                                || (recordInfoList.get(recordInfoList.size() - 1).getStart_time()
                                + recordInfoList.get(recordInfoList.size() - 1).getLength()) > endDayTime
                                || recordInfoList.size() < 30) {
                            for (int i = 0; i < recordInfoList.size(); i++) {
                                if (recordInfoList.get(i).getStart_time() < endDayTime) {
                                    if (recordInfoList.get(i).getLength() > 0) {
                                        RecordInfo info = recordInfoList.get(i);
                                        if (info.getStart_time() < startDayTime) {
                                            if (info.getStart_time() + info.getLength() < startDayTime) {
                                                continue;
                                            }
                                            if (info.getStart_time() + info.getLength() < endDayTime) {
                                                info.setLength((int) (info.getLength() - (startDayTime - info.getStart_time())));
                                                info.setStart_time(startDayTime);
                                            } else {
                                                info.setLength(24 * 3600 * 1000);
                                                info.setStart_time(startDayTime);
                                            }
                                        } else {
                                            if (info.getStart_time() + info.getLength() > endDayTime) {
                                                info.setLength((int) (endDayTime - info.getStart_time()));
                                            }
                                        }
                                        cloudRecordInfoList.add(new CloudRecordInfo(id, 1, info.getStart_time() * 1000, info.getLength() * 1000, RecordType.getType(info.getRecord_type())));
                                    }
                                } else {
                                    continue;
                                }
                            }

                        } else {
                            for (RecordInfo recordInfo : recordInfoList) {
                                if (recordInfo.getLength() > 0) {
                                    if (recordInfo.getStart_time() < startDayTime) {
                                        recordInfo.setLength((int) (recordInfo.getLength() - (startDayTime - recordInfo.getStart_time())));
                                        recordInfo.setStart_time(startDayTime);
                                        cloudRecordInfoList.add(
                                                new CloudRecordInfo(id, 1, recordInfo.getStart_time() * 1000,
                                                        recordInfo.getLength() * 1000, RecordType.getType(recordInfo.getRecord_type())));
                                    } else {
                                        cloudRecordInfoList.add(
                                                new CloudRecordInfo(id, 1, recordInfo.getStart_time() * 1000,
                                                        recordInfo.getLength() * 1000, RecordType.getType(recordInfo.getRecord_type())));
                                    }
                                }
                            }
                            timestamp = recordInfoList.get(recordInfoList.size() - 1).getStart_time()
                                    + recordInfoList.get(recordInfoList.size() - 1).getLength() + 1;
                            if (timestamp < endDayTime){

                                getSdRecordList(timestamp * 1000, channel);// 递归请求
                                return Observable.error(new Throwable());
                            }
                        }

                        return null;
                    }})
                .subscribe(new Subscriber<RecordInfo>() {
                    @Override
                    public void onCompleted() {
                        if (onCallbackListener !=  null){
                            onCallbackListener.onRecordListCallback(cloudRecordInfoList);
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.d("", "");
                    }

                    @Override
                    public void onNext(RecordInfo recordInfo) {
                    }
                });
    }

    @Override
    public void getSDRecordPlayInfo(long timestamp, int channel) {
        super.getSDRecordPlayInfo(timestamp, channel);
        if (onCallbackListener !=  null){
            SdRecord sdRecord = new SdRecord();
            sdRecord.setChannelNum(channel);
            sdRecord.setId(id + timestamp/1000);
            sdRecord.setStartTime(timestamp/1000);
            sdRecordList.add(sdRecord);

            List<SdRecord> sdRecords = new ArrayList<>();
            sdRecords.add(sdRecord);
            SdRecordDevice sdRecordDevice = new SdRecordDevice(device, sdRecords);
            onCallbackListener.onSDRecordStartCallback(sdRecordDevice);
            Log.d("SdRecord", "getSdRecordInfo: startTime = " + sdRecord.getStartTime());
        }
    }

    @Override
    public void getSDRecordPlayInfo(long timestamp) {
        super.getSDRecordPlayInfo(timestamp);
        getSDRecordPlayInfo(timestamp, 1);
    }

    @Override
    public void getCloudRecordList(long timestamp, int channel) {
        super.getCloudRecordList(timestamp, channel);
        cloudRecordInfoList.clear();
        getDeviceService(timestamp, true, channel);
    }

    @Override
    public void getCloudRecordList(final long timestamp) {
        getCloudRecordList(timestamp, currentVideoDevice.getChan_num());
    }

    private boolean isHasGetDeviceServers;
    private void getDeviceService(final long timestamp, final boolean isGetCloudRecord, final int channel) {
        isHasGetDeviceServers = true;
        this.timestamp = timestamp;
        final long startDayTime = timestamp;
        final long endDayTime = startDayTime + 24 * 3600 * 1000;
        Danale.get().getCloudService().getDeviceServers(2, Arrays.asList(new String[]{id}))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<GetDeviceServersResult>() {
                    @Override
                    public void onCompleted() {
                        if (isGetCloudRecord){
                            getCloudRecordList(timestamp, startDayTime, endDayTime, channel);
                        }else {
                            getCloudRecordPlayInfoAfter(timestamp, channel);
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        if (isGetCloudRecord){
                            getCloudRecordList(timestamp, startDayTime, endDayTime, channel);
                        }else {
                            getCloudRecordPlayInfoAfter(timestamp, channel);
                        }
                    }

                    @Override
                    public void onNext(GetDeviceServersResult getDeviceServersResult) {

                    }
                });
    }

    private void getCloudRecordList(final long timestamp, final long startDayTime, final long endDayTime, int channel) {

        Danale.get().getCloudService().getCloudRecordList(1, id, channel, timestamp, 1, 65535)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(new Func1<GetCloudRecordListResult, Observable<CloudRecord>>() {
                    @Override
                    public Observable<CloudRecord> call(GetCloudRecordListResult getCloudRecordListResult) {
                        List<CloudRecord> cloudRecordList = getCloudRecordListResult.getCloudRecordList();
                        if (cloudRecordList == null){
                            Observable.error(new Throwable());
                        }
                        return Observable.from(cloudRecordList);
                    }
                })
                .subscribe(new Subscriber<CloudRecord>() {
                    @Override
                    public void onCompleted() {

                        if (onCallbackListener != null){
                            onCallbackListener.onRecordListCallback(cloudRecordInfoList);
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        if (onCallbackListener != null){
                            onCallbackListener.onRecordListCallback(cloudRecordInfoList);
                        }
                    }

                    @Override
                    public void onNext(CloudRecord cloudRecord) {
                        CloudRecordInfo recordInfo = new CloudRecordInfo();
                        if (cloudRecord.getStartTime() < startDayTime) {
                            if (cloudRecord.getStartTime() + cloudRecord.getLength() >= startDayTime) {
                                if (cloudRecord.getStartTime() + cloudRecord.getLength() > endDayTime) {
                                    cloudRecord.setStartTime(startDayTime);
                                    cloudRecord.setLength(24 * 3600 * 1000);
                                } else {
                                    cloudRecord
                                            .setLength(cloudRecord.getLength() - (startDayTime - cloudRecord.getStartTime()));
                                    cloudRecord.setStartTime(startDayTime);
                                }
                            }
                        } else {
                            if (cloudRecord.getStartTime() <= endDayTime) {
                                if (cloudRecord.getStartTime() + cloudRecord.getLength() > endDayTime) {
                                    cloudRecord.setLength(endDayTime - cloudRecord.getStartTime());
                                }
                            }
                        }

                        recordInfo.setStartTime(cloudRecord.getStartTime());
                        recordInfo.setTimeLen(cloudRecord.getLength());
                        recordInfo.setRecordType(cloudRecord.getType() == CloudRecordType.PLAN
                                ? RecordType.PLAN_RECORD
                                : RecordType.ALERT_RECORD);
                        cloudRecordInfoList.add(recordInfo);
                    }
                });
    }

    private void getCloudRecordPlayInfoAfter(long timestamp, int channel){
        cloudRecordPlayInfoList.clear();
        Danale.get().getCloudService().getCloudRecordPlayInfo(1, id, channel, timestamp)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(new Func1<GetCloudRecordPlayInfoResult, Observable<CloudRecordPlayInfo>>() {
                    @Override
                    public Observable<CloudRecordPlayInfo> call(GetCloudRecordPlayInfoResult getCloudRecordPlayInfoResult) {
                        return Observable.from(getCloudRecordPlayInfoResult.getCloudRecordPlayInfoList());
                    }
                })
                .subscribe(new Subscriber<CloudRecordPlayInfo>() {
                    @Override
                    public void onCompleted() {
                        cloudRecordDevice = new CloudRecordDevice(DeviceCache.getInstance().getDevice(id), cloudRecordPlayInfoList);
                        if (onCallbackListener != null){
                            onCallbackListener.onCloudRecordStartCallback(true, 0, cloudRecordDevice);
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        if (e instanceof PlatformApiError){
                            int errorCode = ((PlatformApiError) e).getPlatformErrorCode();
                            onCallbackListener.onCloudRecordStartCallback(true, errorCode, cloudRecordDevice);
                        }
                        Log.d("", "");
                    }

                    @Override
                    public void onNext(CloudRecordPlayInfo cloudRecordPlayInfo) {
                        String cloud_id = id + cloudRecordPlayInfo.getStartTime();
                        cloudRecordPlayInfo.setId(cloud_id);
                        cloudRecordPlayInfoList.add(cloudRecordPlayInfo);
                    }
                });
    }

    @Override
    public void getCloudRecordPlayInfo(long timestamp, int channel) {
        super.getCloudRecordPlayInfo(timestamp, channel);
        if (isHasGetDeviceServers){
            getCloudRecordPlayInfoAfter(timestamp, channel);
        }else {
            getDeviceService(timestamp, false, channel);
        }
    }

    @Override
    public void getCloudRecordPlayInfoByPushMsg(PushMsg pushMsg) {
        super.getCloudRecordPlayInfoByPushMsg(pushMsg);
        if (pushMsg.getRecordPath().contains("https://")){
            pushMsg.setRecordPath(pushMsg.getRecordPath().substring("https://".length(), pushMsg.getRecordPath().length()));
        }
        if (CloudRecordPlayback.isMsgLinkToRecord(pushMsg)){
            cloudRecordPlayInfoList.clear();

            cloudRecordPlayInfoList.add(pushMsg2CloudRecordPlayInfo(pushMsg));
            cloudRecordDevice = new CloudRecordDevice(DeviceCache.getInstance().getDevice(id), cloudRecordPlayInfoList);
            if (onCallbackListener != null){
                onCallbackListener.onCloudRecordStartCallback(true, 0, cloudRecordDevice);
            }
        }else {
            getCloudRecordPlayInfo(pushMsg.getCreateTime(), pushMsg.getChannel());
        }

    }

    @NonNull
    private CloudRecordPlayInfo pushMsg2CloudRecordPlayInfo(PushMsg pushMsg) {
        String savePath = pushMsg.getRecordPath();
        int bucketSplitIndex = savePath.indexOf(".");
        final String bucket = savePath.substring(0, bucketSplitIndex);
//        int hostSplitIndex = savePath.indexOf(":");
        int fileNameFirstSplit = savePath.indexOf("/");
        final String host = savePath.substring(bucketSplitIndex + 1, fileNameFirstSplit);
        final String fileName = savePath.substring(fileNameFirstSplit + 1);

        CloudRecordPlayInfo cloudRecordPlayInfo = new CloudRecordPlayInfo();
        cloudRecordPlayInfo.setBucket(bucket);
        cloudRecordPlayInfo.setHost(host);
        cloudRecordPlayInfo.setPath(fileName);
        cloudRecordPlayInfo.setOffset(pushMsg.getRecordTimeLen());
        cloudRecordPlayInfo.setId(id + pushMsg.getCreateTime());
        return cloudRecordPlayInfo;
    }

    @Override
    public void getCloudRecordPlayInfo(long timestamp){
        getCloudRecordPlayInfo(timestamp, currentVideoDevice.getChan_num());
    }

    @NonNull
    private CmdDeviceInfo getCmdDeviceInfo() {
        CmdDeviceInfo cmdDeviceInfo = new CmdDeviceInfo();
        cmdDeviceInfo.setDevice_id(id);
        cmdDeviceInfo.setDevice_type(DeviceType.IPC);
        cmdDeviceInfo.setLive_type(LiveType.NORMAL);
        cmdDeviceInfo.setDevice_name("admin");
        cmdDeviceInfo.setDevice_pass("admin");
        return cmdDeviceInfo;
    }
}
